import { join } from 'path' // 导入路径处理模块
import jsYaml from 'js-yaml' // 导入 YAML 解析模块，用于解析 YAML 格式的配置文件
import merge from 'lodash/merge' // 导入 lodash 的 merge 方法，用于深度合并多个配置对象
import { readFileSync, existsSync } from 'fs' // 导入文件系统模块，用于读取文件和检查文件是否存在

/**
 * 读取指定路径的 YAML 配置文件并转为对象类型
 * @param filePath YAML 配置文件的路径
 * @returns 解析后的配置对象，如果文件不存在则返回空对象
 */
function _loadConfig(filePath: string): Recordable {
  return existsSync(filePath) ? (jsYaml.load(readFileSync(filePath, 'utf-8')) as Recordable) : {}
}

/**
 * 加载并合并应用的所有配置文件
 * 参考文档: http://nestjs.inode.club/techniques/configuration#自定义配置文件
 * @returns 合并后的最终配置对象
 *
 * 配置加载优先级(从低到高):
 *  1. 通用配置 (config.yaml)
 *  2. 通用本地配置 (config.local.yaml)
 *  3. 环境特定配置 (config.{NODE_ENV}.yaml)
 *  4. 环境特定本地配置 (config.{NODE_ENV}.local.yaml)
 * 后面的配置会覆盖前面配置中的同名属性
 */

export default (): Recordable => {
  // 从环境变量中获取当前运行环境，默认为开发环境
  const { NODE_ENV = 'development' } = process.env

  // 确定配置文件的存放目录
  //  - 开发环境: 读取项目根目录下的 config 文件夹
  //  - 生产环境: 直接读取当前文件所在的目录
  const CONFIG_DIR_PATH = NODE_ENV === 'development' ? join(__dirname, '../config') : __dirname

  // 定义各类配置文件的路径
  // 通用配置文件（所有环境共享的基础配置）
  const YAML_COMMON_CONFIG_PATH = join(CONFIG_DIR_PATH, 'config.yaml')
  // 通用本地配置文件（本地开发覆盖通用配置的个性化设置，不应提交到代码仓库）
  const YAML_COMMON_CONFIG_LOCAL_PATH = join(CONFIG_DIR_PATH, 'config.local.yaml')
  // 特定环境的配置文件（针对当前环境的专用配置，如开发/生产环境的差异配置）
  const YAML_ENV_CONFIG_PATH = join(CONFIG_DIR_PATH, `config.${NODE_ENV || 'development'}.yaml`)
  // 特定环境的本地配置文件（本地覆盖当前环境配置的个性化设置，不应提交到代码仓库）
  const YAML_ENV_CONFIG_LOCAL_PATH = join(CONFIG_DIR_PATH, `config.${NODE_ENV || 'development'}.local.yaml`)

  // 读取各类配置文件的内容
  const COMMON_CONFIG = _loadConfig(YAML_COMMON_CONFIG_PATH)
  const COMMON_CONFIG_LOCAL = _loadConfig(YAML_COMMON_CONFIG_LOCAL_PATH)
  const ENV_CONFIG = _loadConfig(YAML_ENV_CONFIG_PATH)
  const ENV_CONFIG_LOCAL = _loadConfig(YAML_ENV_CONFIG_LOCAL_PATH)

  // 深度合并配置，后面的配置会覆盖前面的同名配置
  // 这样既保证了基础配置的通用性，又允许环境和本地配置进行个性化定制
  return merge(COMMON_CONFIG, COMMON_CONFIG_LOCAL, ENV_CONFIG, ENV_CONFIG_LOCAL)
}
